﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Screens;
using Microsoft.Xna.Framework.Content;
using GameLib;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using EMK.Cartography;
using EMK.LightGeometry;
using DustObjects;
using MersenneTwister;

namespace DustGame {
    class GameplayScreen : GameScreen {

        eGameMode gameMode = eGameMode.BOARDSETUP;
        eGamePhase gamePhase = eGamePhase.PHASE_CAPITAL_SELECTION;

        ContentManager content;
        Camera2D camera;
        Texture2D background;
        Texture2D tankTexture;
        Texture2D mechTexture;
        Texture2D submarineTexture;
        Texture2D fighterTexture;
        Texture2D productionTexture;

        Texture2D flagEngland;

        Sprite circle = new Sprite();
        Sprite mouse = new Sprite();
        Color[] colors = new Color[4];
        Player[] players = new Player[4];
        int colorIndex = 0;

        Vector2 mouseposition;
        Board board;
        DustNode MouseHoverNode;
        float scalePulse;
        float totalSecondsElapsed;


        List<DustNode> capitals = new List<DustNode>();
        List<DustNode> lands = new List<DustNode>();
        List<DustNode> gisements = new List<DustNode>();

        public GameplayScreen() {
            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);

            colors[0] = Color.Black;
            colors[1] = Color.Red;
            colors[2] = Color.Yellow;
            colors[3] = Color.BlueViolet;

            players[0] = new Player("Player1");
            players[1] = new Player("Player2");
            players[2] = new Player("Player3");
            players[3] = new Player("Player4");

            gameMode = eGameMode.BOARDSETUP;
            gamePhase = eGamePhase.PHASE_CAPITAL_SELECTION;
        }

        public override void LoadContent() {
            Viewport view = ScreenManager.GraphicsDevice.Viewport;
            camera = new Camera2D(ScreenManager.Game);
            ScreenManager.Game.Components.Add(camera);

            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");

            background = content.Load<Texture2D>("dust_boardgame");
            circle.Texture = content.Load<Texture2D>("circle");

            tankTexture = content.Load<Texture2D>("tank");
            mechTexture = content.Load<Texture2D>("mech");
            submarineTexture = content.Load<Texture2D>("submarine");
            fighterTexture = content.Load<Texture2D>("fighter");
            productionTexture = content.Load<Texture2D>("production");

            flagEngland = content.Load<Texture2D>("England");

            mouse.Texture = content.Load<Texture2D>("mouse");
            mouse.Position = new Vector2(view.Width / 2, view.Height / 2);// new Vector2(background.Width / 2, background.Height / 2);
            mouse.Origin += new Vector2(-18f, -26f);
            camera.MoveSpeed = 8f;
            camera.Focus = mouse;
            camera.Scale = 0.61f;


            board = new Board("Content/dust_path.bin");
            // faire une liste de toutes les capitales
            foreach (DustNode node in board.G.Nodes) {
                if (node.TypeCase == EnumDustType.CAPITAL) {
                    capitals.Add(node);
                }
                else if (node.Terrain == EnumDustTerrain.TERRE) {
                    lands.Add(node);
                    if (node.TypeCase == EnumDustType.GISEMENT)
                        gisements.Add(node);
                }
            }

            ScreenManager.Game.ResetElapsedTime();
        }

        public override void HandleInput(InputState input) {
            PlayerIndex playerIndex;
            if (input.IsMenuCancel(ControllingPlayer, out playerIndex)) {
                this.ExitScreen();
                ScreenManager.Game.Exit();
            }

            //HandleInputDebug(input);
            HandleInputBoardSetup(input);
        }

        public void HandleInputDebug(InputState input) {
            PlayerIndex playerIndex;
            Color color = colors[colorIndex];

            if (input.IsSelect(ControllingPlayer, out playerIndex)) {
                colorIndex = (colorIndex + 1) % colors.Length;
            }
            else if (input.IsNewKeyPress(Keys.T, ControllingPlayer, out playerIndex)) {
                // placer un tank sur la case survolée
                if (MouseHoverNode != null) {
                    if (MouseHoverNode.Terrain == EnumDustTerrain.TERRE) {
                        if (MouseHoverNode.Units.Empty) MouseHoverNode.Units.Color = color;
                        MouseHoverNode.Units.Tanks++;
                    }
                }
            }
            else if (input.IsNewKeyPress(Keys.M, ControllingPlayer, out playerIndex)) {
                // placer un tank sur la case survolée
                if (MouseHoverNode != null) {
                    if (MouseHoverNode.Terrain == EnumDustTerrain.TERRE) {
                        if (MouseHoverNode.Units.Empty) MouseHoverNode.Units.Color = color;
                        MouseHoverNode.Units.Mechs++;
                    }
                }
            }
            else if (input.IsNewKeyPress(Keys.F, ControllingPlayer, out playerIndex)) {
                // placer un tank sur la case survolée
                if (MouseHoverNode != null) {
                    if (MouseHoverNode.Terrain == EnumDustTerrain.TERRE) {
                        if (MouseHoverNode.Units.Empty) MouseHoverNode.Units.Color = color;
                        MouseHoverNode.Units.Fighters++;
                    }
                }
            }
            else if (input.IsNewKeyPress(Keys.G, ControllingPlayer, out playerIndex)) {
                // placer un tank sur la case survolée
                if (MouseHoverNode != null) {
                    if (MouseHoverNode.Terrain == EnumDustTerrain.MER) {
                        if (MouseHoverNode.Units.Empty) MouseHoverNode.Units.Color = color;
                        MouseHoverNode.Units.Submarines++;
                    }
                }
            }
            else if (input.IsNewKeyPress(Keys.P, ControllingPlayer, out playerIndex)) {
                // placer un tank sur la case survolée
                if (MouseHoverNode != null) {
                    if (MouseHoverNode.Terrain == EnumDustTerrain.TERRE && MouseHoverNode.TypeCase != EnumDustType.GISEMENT) {
                        if (MouseHoverNode.Units.Empty) MouseHoverNode.Units.Color = color;
                        MouseHoverNode.Units.Production = !MouseHoverNode.Units.Production;
                    }
                }
            }
        }

        int production = 0;
        public void HandleInputBoardSetup(InputState input) {
            PlayerIndex playerIndex;
            Color currentColor = colors[colorIndex];

            if (input.CurrentMouseState.LeftButton == ButtonState.Pressed && totalSecondsElapsed > .25) {
                totalSecondsElapsed = 0;
                if (gamePhase == eGamePhase.PHASE_CAPITAL_SELECTION) {
                    #region selection d'une capital
                    if (MouseHoverNode.TypeCase == EnumDustType.CAPITAL) {
                        MouseHoverNode.Units.Color = currentColor;
                        MouseHoverNode.Units.Tanks++;

                        capitals.Remove(MouseHoverNode);
                        players[colorIndex].ReserveUnits.Tanks--;

                        #region IA
                        for (int i = 0; i < 3; i++) {
                            colorIndex = (colorIndex + 1) % colors.Length;
                            currentColor = colors[colorIndex];
                            int idx = Alea.Instance.RandomRange(0, capitals.Count - 1);
                            capitals[idx].Units.Color = currentColor;
                            capitals[idx].Units.Tanks++;
                            capitals.Remove(capitals[idx]);
                            players[colorIndex].ReserveUnits.Tanks--;
                        }

                        foreach (DustNode node in capitals) {
                            node.Units.Color = Color.WhiteSmoke;
                            node.Units.Tanks++;
                            node.Units.Mechs++;
                            node.Units.Production = true;
                        }
                        capitals.Clear();
                        colorIndex = 0;
                        #endregion

                        gamePhase = eGamePhase.PHASE_LAND_SELECTION;
                        ScreenManager.AddScreen(new MessageBoxScreen("Annexer les territoires"), ControllingPlayer);
                    }
                    #endregion
                }
                else if (gamePhase == eGamePhase.PHASE_LAND_SELECTION) {
                    #region sélection des terrains
                    if (lands.Count > 0) {
                        if (MouseHoverNode.Terrain == EnumDustTerrain.TERRE && MouseHoverNode.Units.Empty) {
                            MouseHoverNode.Units.Color = currentColor;
                            MouseHoverNode.Units.Tanks++;
                            players[colorIndex].ReserveUnits.Tanks--;

                            lands.Remove(MouseHoverNode);

                            if (gisements.Contains(MouseHoverNode)) {
                                gisements.Remove(MouseHoverNode);
                                players[colorIndex].EnergySources++;
                            }

                            #region IA
                            for (int i = 0; i < 3; i++) {
                                colorIndex = (colorIndex + 1) % colors.Length;
                                currentColor = colors[colorIndex];
                                if (lands.Count == 0) break;

                                int idx;
                                DustNode node;
                                if (gisements.Count > 0) {
                                    idx = Alea.Instance.RandomRange(0, gisements.Count - 1);
                                    node = gisements[idx];
                                }
                                else {
                                    idx = Alea.Instance.RandomRange(0, lands.Count - 1);
                                    node = lands[idx];
                                }
                                node.Units.Color = currentColor;
                                node.Units.Tanks++;
                                players[colorIndex].ReserveUnits.Tanks--;

                                lands.Remove(node);

                                if (gisements.Contains(node)) {
                                    gisements.Remove(node);
                                    players[colorIndex].EnergySources++;
                                }
                            }
                            colorIndex = 0;
                            #endregion

                            if (lands.Count == 0) {
                                gamePhase = eGamePhase.PHASE_PRODUCTION_CENTER;
                                ScreenManager.AddScreen(new MessageBoxScreen("Placer 3 centres de productions"), ControllingPlayer);
                            }
                        }
                    }
                    #endregion
                }
                else if (gamePhase == eGamePhase.PHASE_PRODUCTION_CENTER) {
                    #region placer les centres de production
                    // pas de centre de production sur les case gisement
                    if (MouseHoverNode.TypeCase != EnumDustType.GISEMENT) {
                        // si sur une case qui possède déjà un gisement
                        if (!MouseHoverNode.Units.Empty && !MouseHoverNode.Units.Production && MouseHoverNode.Units.Color == currentColor) {
                            MouseHoverNode.Units.Production = true;
                            production++;
                            players[colorIndex].ProductionPlants++;

                            #region IA
                            for (int i = 0; i < 3; i++) {
                                colorIndex = (colorIndex + 1) % colors.Length;
                                currentColor = colors[colorIndex];

                                List<DustNode> nodes = new List<DustNode>();
                                foreach (DustNode n in board.G.Nodes) {
                                    if (n.Terrain == EnumDustTerrain.TERRE && !n.Units.Production
                                        & n.Units.Color == colors[colorIndex]) {
                                        nodes.Add(n);
                                    }
                                }
                                int idx = Alea.Instance.RandomRange(0, nodes.Count - 1);
                                nodes[idx].Units.Production = true;
                            }
                            colorIndex = 0;
                            #endregion

                            if (production > 2) {
                                gamePhase = eGamePhase.PHASE_REINFORCEMENT;
                                MessageBoxScreen msg = new MessageBoxScreen("Renforcement!");
                                msg.Accepted += new EventHandler<PlayerIndexEventArgs>(msg_Accepted);
                                ScreenManager.AddScreen(msg, ControllingPlayer);
                            }
                        }
                    }
                    #endregion
                }
            }
        }

        void msg_Accepted(object sender, PlayerIndexEventArgs e) {
            ScreenManager.AddScreen(new ProductionScreen(), ControllingPlayer);
        }

        bool isdragging = false;
        Vector2 origin = Vector2.Zero;
        Vector2 delta = Vector2.Zero;

        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen) {
            Viewport view = ScreenManager.GraphicsDevice.Viewport;


            // cette variable est utilisé comme tampon pour les tests d'appuie de touches
            totalSecondsElapsed += (float)gameTime.ElapsedGameTime.TotalSeconds;

            // Pulsate the size of the selected menu entry.
            double time = gameTime.TotalGameTime.TotalSeconds;
            float pulsate = (float)Math.Sin(time * 6) + 1;
            scalePulse = 1 + pulsate * 0.5f;

            MouseState ms = Mouse.GetState();
            if (ms.RightButton == ButtonState.Pressed) {
                if (isdragging == false) {
                    isdragging = true;
                    origin = new Vector2(ms.X, ms.Y);
                }
                else {
                    delta = origin - new Vector2(ms.X,ms.Y);
                    origin = new Vector2(ms.X,ms.Y);

                    mouse.Position += delta * camera.MoveSpeed;
                }
            }
            else {
                isdragging = false;
            }

            #region translation controls, left stick xbox or ZQSD keyboard
            if (Keyboard.GetState().IsKeyDown(Keys.Q)
                || GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.X < 0) {

                if (mouse.Position.X <= view.Width/2)
                    mouse.Position.X = view.Width / 2;
                else
                    mouse.Position.X -= camera.MoveSpeed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.D)
                || GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.X > 0) {

                    if (mouse.Position.X >= (2048 - view.Width + view.Width / 2))
                        mouse.Position.X = (2048 - view.Width + view.Width / 2);
                else
                    mouse.Position.X += camera.MoveSpeed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.S)
                || GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.Y < 0) {

                    if (mouse.Position.Y >= (1489 - view.Height + view.Height / 2))
                        mouse.Position.Y = (1489 - view.Height + view.Height / 2);
                else
                    mouse.Position.Y += camera.MoveSpeed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Z)
                || GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.Y > 0) {

                    if (mouse.Position.Y <= view.Height / 2)
                        mouse.Position.Y = view.Height / 2;
                else
                    mouse.Position.Y -= camera.MoveSpeed;
            }
            #endregion

            #region zoom/scale controls, left/right triggers or CW keyboard
            if (Keyboard.GetState().IsKeyDown(Keys.C)
                /*|| GamePad.GetState(PlayerIndex.One).Triggers.Right > 0*/) { camera.Scale += camera.ScaleSpeed; }
            if (Keyboard.GetState().IsKeyDown(Keys.W)
                /*|| GamePad.GetState(PlayerIndex.One).Triggers.Left > 0*/) { camera.Scale -= camera.ScaleSpeed; }
            #endregion

            if (IsActive) {
                // TODO: this game isn't very fun! You could probably improve
                // it by inserting something more interesting in this space :-)

                // récupération des coordonnées de la souris
                //MouseState ms = Mouse.GetState();
                mouseposition = new Vector2(ms.X, ms.Y);
                MouseHoverNode = (DustNode)board.NoeudSelonCameraPosition((int)ms.X, (int)ms.Y, camera);
            }

            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
        }

        public override void Draw(GameTime gameTime) {
            // This game has a blue background. Why? Because!
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
                                               Color.CornflowerBlue, 0, 0);

            Viewport view = ScreenManager.GraphicsDevice.Viewport;
            SpriteFont gameFont = Fonts.Normal;
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
            Color color;

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.None, camera.Transform);

            spriteBatch.Draw(background, Vector2.Zero * camera.Scale, null, Color.White, 0.0f, Vector2.Zero, camera.Scale, SpriteEffects.None, 0.0f);

            if (IsActive) {
                if (gamePhase == eGamePhase.PHASE_CAPITAL_SELECTION) {
                    foreach (DustNode n in capitals) {
                        spriteBatch.Draw(circle.Texture, n.Location * camera.Scale, null, Color.Red, 0.0f, circle.Origin, scalePulse * camera.Scale, SpriteEffects.None, 0.0f);
                    }
                }
                else if (gamePhase == eGamePhase.PHASE_LAND_SELECTION) {
                    foreach (DustNode n in lands) {
                        spriteBatch.Draw(circle.Texture, n.Location * camera.Scale, null, Color.Red, 0.0f, circle.Origin, scalePulse * camera.Scale, SpriteEffects.None, 0.0f);
                    }
                }
                else {
                    if (MouseHoverNode != null) {
                        foreach (DustNode n in MouseHoverNode.AccessibleNodes) {
                            if (n.Units.Empty)
                                color = Color.YellowGreen;
                            else {
                                color = n.Units.Color;
                            }
                            color.A = 127;
                            spriteBatch.Draw(circle.Texture, n.Location * camera.Scale, null, color, 0.0f, circle.Origin, scalePulse * camera.Scale, SpriteEffects.None, 0.0f);
                        }
                    }
                }
            }


            // Afficher toutes les cases dans des couleurs différentes en fonction du type
            float scale = 1f;
            foreach (DustNode n in board.G.Nodes) {

                #region afficher les cercles dans différentes couleurs en fonction du type
                //if (n.TypeCase == EnumDustType.CAPITAL)
                //    color = Color.Green;
                //else if (n.TypeCase == EnumDustType.GISEMENT)
                //    color = Color.Gray;
                //else if (n.Terrain == EnumDustTerrain.MER)
                //    color = Color.Blue;
                //else
                //    color = Color.White;
                #endregion

                color = n.Units.Color;
                if (!n.Units.Empty) {
                    color.A = 127;
                    spriteBatch.Draw(circle.Texture, n.Location * camera.Scale, null, color, 0.0f, circle.Origin, 1.8f * camera.Scale, SpriteEffects.None, 0.0f);
                }

                if (n.Units.Production) {
                    Vector2 v2 = n.Location;
                    //if (n.Units.Tanks > 0 || n.Units.Mechs > 0) {
                    //    v2.X -= 35 * scale;
                    //    v2.Y -= 90 * scale;
                    //}
                    //else {
                    v2.X -= 35 * scale;
                    v2.Y -= 80 * scale;
                    //}
                    spriteBatch.Draw(productionTexture, v2 * camera.Scale, null, Color.White, 0.0f, Vector2.Zero, camera.Scale * scale, SpriteEffects.None, 0.0f);
                }
                if (n.Units.Tanks > 0) {
                    Vector2 v2 = n.Location;
                    if (n.Units.Mechs > 0 || n.Units.Fighters > 0) {
                        v2.X -= 80 * scale;
                        v2.Y -= 30 * scale;
                    }
                    else {
                        v2.X -= 31 * scale;
                        v2.Y -= 30 * scale;
                    }
                    spriteBatch.Draw(tankTexture, v2 * camera.Scale, null, Color.White, 0.0f, Vector2.Zero, camera.Scale * scale, SpriteEffects.None, 0.0f);
                }
                if (n.Units.Mechs > 0) {
                    Vector2 v2 = n.Location;
                    if (n.Units.Tanks > 0 || n.Units.Fighters > 0) {
                        v2.X -= 30 * scale;
                        v2.Y += 10 * scale;
                    }
                    else {
                        v2.X -= 31 * scale;
                        v2.Y -= 30 * scale;
                    }
                    spriteBatch.Draw(mechTexture, v2 * camera.Scale, null, Color.White, 0.0f, Vector2.Zero, camera.Scale * scale, SpriteEffects.None, 0.0f);
                }
                if (n.Units.Fighters > 0) {
                    Vector2 v2 = n.Location;
                    if (n.Units.Tanks > 0 || n.Units.Mechs > 0) {
                        v2.X += 10 * scale;
                        v2.Y -= 30 * scale;
                    }
                    else {
                        v2.X -= 31 * scale;
                        v2.Y -= 30 * scale;
                    }
                    spriteBatch.Draw(fighterTexture, v2 * camera.Scale, null, Color.White, 0.0f, Vector2.Zero, camera.Scale * scale, SpriteEffects.None, 0.0f);
                }
                if (n.Units.Submarines > 0) {
                    Vector2 v2 = n.Location;
                    v2.X -= 31 * scale;
                    v2.Y -= 30 * scale;
                    spriteBatch.Draw(submarineTexture, v2 * camera.Scale, null, Color.White, 0.0f, Vector2.Zero, camera.Scale * scale, SpriteEffects.None, 0.0f);
                }
            }

            spriteBatch.End();



            spriteBatch.Begin();

            spriteBatch.Draw(mouse.Texture, mouseposition, null, Color.White, 0.0f, mouse.Origin, .5f, SpriteEffects.None, 0.0f);

            Vector2 v = Vector2.Zero;
            // afficher les coordonnées de la souris
            spriteBatch.DrawString(gameFont, "background " + mouse.Position.ToString(), v, Color.Red);

            // afficher la souris
            //spriteBatch.Draw(mouse.Texture, new Vector2(mX, mY), null, Color.White, 0.0f, mouse.Origin + new Vector2(-18f, -26f), .5f, SpriteEffects.None, 0.0f);
            v.Y += gameFont.LineSpacing;
            spriteBatch.DrawString(gameFont, "mouse cursor:" +  mouseposition.ToString(), v, Color.Red);

            v.Y += gameFont.LineSpacing;
            spriteBatch.DrawString(gameFont, "mouseposition:" + mouse.Position, v, Color.Red);

            if (MouseHoverNode != null) {
                v += new Vector2(gameFont.LineSpacing);
                spriteBatch.DrawString(gameFont, MouseHoverNode.Location.ToString() +
                    "\r\n" + MouseHoverNode.Terrain +
                    "\r\n" + MouseHoverNode.TypeCase +
                    "\r\n" + MouseHoverNode.Units.ToString()
                    , v, Color.WhiteSmoke);
            }
            spriteBatch.End();
        }

        /// <summary>
        /// Retourner le Node correspondant aux coordonnées en prenant en compte
        /// les transformations liée à la position et au zoom de la caméra
        /// </summary>
        public DustNode NoeudSelonCameraPosition(int X, int Y, Matrix Transform) {
            DustNode NodeMin = null;
            double DistanceMin = -1;
            Point3D P = new Point3D(X, Y, 0);

            Viewport view = ScreenManager.GraphicsDevice.Viewport;
            Vector2 offset = new Vector2(view.Width / 2, view.Height / 2);

            foreach (DustNode N in board._graph.Nodes) {
                //Vector2 origin = new Vector2(backgroundTex.Width / 2, backgroundTex.Height / 2);
                Vector2 v2 = new Vector2((float)N.Position.X, (float)N.Position.Y) - offset;
                v2 *= camera.Scale;

                // en application une transformation à partir de la matrice de la caméra
                // sur la position du pion, on retrouve les coordonnées écran
                v2 = Vector2.Transform(v2, Transform);

                Point3D point = new Point3D(v2.X, v2.Y, 0);
                double DistanceTemp = Point3D.DistanceBetween(point, P);
                if (DistanceMin == -1 || DistanceMin > DistanceTemp) {
                    DistanceMin = DistanceTemp;
                    NodeMin = N;
                }
            }
            //Distance = DistanceMin;
            return (DustNode)NodeMin;
        }
    }
}
